/*
 * TradingConfig.h
 *
 *  Created on: Apr 19, 2016
 *      Author: jamil
 */

#ifndef TRADINGCONFIG_H_
#define TRADINGCONFIG_H_

#include "ConfigReader.h"
#include "../avicenna/AvicennaInterface.h"
#include "../trade/TradeInstance.h"


enum TRADE_ACCOUNT_TYPE {
	TA_SINGLE,
	TA_PAIR,

};

struct AvicennaConfiguration {
	int dll_timer1;
	string dll_ip;
	bool dll_input;
	string dll_str1;
	string dll_str2;
	string dll_str3;
	string dll_str4;
	string dll_str5;

};


struct TradeAccount {
	string id;

	TRADE_ACCOUNT_TYPE type;
	// in case if account type is TA_SINGLE
	string username;	//only username should be there


	//in case if account type is TA_PAIR
	string long_acc;	//long account id
	string short_acc;	//short account id

	//Timer data
	int legacy_timer;
	int legacy_trader_timer;
	int legacy_pit_timer;

	uint32_t maximum_order;	//< any order generated by the account shouldn't get exceed this value
	uint32_t maximum_live; 	//<maximum live position to be open/close at a time;

	AvicennaConfiguration avicenna_configuration[2];


	std::unique_ptr<AvicennaInterface> avicenna_interface;
	std::unique_ptr<OrderAccount> short_account;
	std::unique_ptr<OrderAccount> long_account;

	using TradeInstances = std::map<std::string, std::unique_ptr<TradeInstance>> ;
	TradeInstances trade_instances;	//symbol name and corresponding trade instances


	std::vector<std::string> venues;
	uint8_t attempts;

	static bool all_enable;
	bool enable_this;

	TradeAccount(ptree &pt): avicenna_interface(nullptr), short_account(nullptr), long_account(nullptr), enable_this(true) {

		ptree attr = pt.get_child("<xmlattr>");
		id = attr.get_child("id").data();

		string temp = attr.get_child("type").data();

		if ( "single" == temp ) {
			type = TA_SINGLE;
			username = pt.get_child("username").data();
			if ( "" == username ) {
				BOOST_THROW_EXCEPTION(ConfigException()<<config_info("single account must have username"));
			}
		}
		else if ( "pair" == temp ) {
			type = TA_PAIR;
			long_acc = pt.get_child("long_account").data();
			short_acc = pt.get_child("short_account").data();
			if ( "" == long_acc || "" == short_acc ) {
				BOOST_THROW_EXCEPTION(ConfigException()<<config_info("pair account must have long and short account ID's"));
			}
		}
		else BOOST_THROW_EXCEPTION(ConfigException()<<config_info("type must be define for account either single or pair"));

		//maximum order size paramter
		maximum_order = pt.get_child("maximum_order").get_value<uint32_t>();
		maximum_live = pt.get_child("maximum_live").get_value<uint32_t>();

		//Avicenna paramters
		legacy_timer = pt.get_child("legacy_timer").get_value<int>();
		if ( 0 == legacy_timer ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("legacy_timer definition missing"));
		legacy_trader_timer = pt.get_child("legacy_trader_timer").get_value<int>();
		if ( 0 == legacy_trader_timer ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("legacy_trader_timer definition missing"));
		legacy_pit_timer = pt.get_child("legacy_pit_timer").get_value<int>();
		if ( 0 == legacy_pit_timer ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("legacy_pit_timer definition missing"));



		auto& avicenna1 = pt.get_child("avicenna1");

		avicenna_configuration[0].dll_timer1 = avicenna1.get_child("dll_timer1").get_value<int>();
		avicenna_configuration[0].dll_ip = avicenna1.get_child("ip").get_value<string>();
		avicenna_configuration[0].dll_input = avicenna1.get_child("dll_input").get_value<string>() == "true"?true:false;
		avicenna_configuration[0].dll_str1 = avicenna1.get_child("dll_str1").get_value<string>();
		avicenna_configuration[0].dll_str2 = avicenna1.get_child("dll_str2").get_value<string>();
		avicenna_configuration[0].dll_str3 = avicenna1.get_child("dll_str3").get_value<string>();
		avicenna_configuration[0].dll_str4 = avicenna1.get_child("dll_str4").get_value<string>();
		avicenna_configuration[0].dll_str5 = avicenna1.get_child("dll_str5").get_value<string>();


		auto& avicenna2 = pt.get_child("avicenna2");

		avicenna_configuration[1].dll_timer1 = avicenna2.get_child("dll_timer1").get_value<int>();
		avicenna_configuration[1].dll_ip = avicenna2.get_child("ip").get_value<string>();
		avicenna_configuration[1].dll_input = avicenna2.get_child("dll_input").get_value<string>() == "true"?true:false;
		avicenna_configuration[1].dll_str1 = avicenna2.get_child("dll_str1").get_value<string>();
		avicenna_configuration[1].dll_str2 = avicenna2.get_child("dll_str2").get_value<string>();
		avicenna_configuration[1].dll_str3 = avicenna2.get_child("dll_str3").get_value<string>();
		avicenna_configuration[1].dll_str4 = avicenna2.get_child("dll_str4").get_value<string>();
		avicenna_configuration[1].dll_str5 = avicenna2.get_child("dll_str5").get_value<string>();


//		dll_timer1 = pt.get_child("dll_timer1").get_value<int>();
//		if ( 0 == dll_timer1 ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("dll_timer1 definition missing"));
//		dll_timer2 = pt.get_child("dll_timer2").get_value<int>();
//		if ( 0 == dll_timer2 ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("dll_timer2 definition missing"));
//		dll_ip = pt.get_child("ip").get_value<string>();
//		if ( "" == dll_ip ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("dll_ip definition missing"));
//		dll_input = pt.get_child("dll_input").get_value<string>() == "true"?true:false;
//
//		dll_str1 = pt.get_child("dll_str1").get_value<string>();
//		if ( "" == dll_str1 ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("dll_str1 definition missing"));
//		dll_str2 = pt.get_child("dll_str2").get_value<string>();
//		if ( "" == dll_str2 ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("dll_str2 definition missing"));
//		dll_str3 = pt.get_child("dll_str3").get_value<string>();
//		if ( "" == dll_str3 ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("dll_str3 definition missing"));
//		dll_str4 = pt.get_child("dll_str4").get_value<string>();
//		if ( "" == dll_str4 ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("dll_str4 definition missing"));
//		dll_str5 = pt.get_child("dll_str5").get_value<string>();
//		if ( "" == dll_str5 ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("dll_str5 definition missing"));


		auto& venues = pt.get_child("venues");

		attempts = venues.get_child("<xmlattr>").get_child("attempts").get_value<int>();
		INFO<<"no of attempts to be made for venues: "<<(int)attempts;

		for (auto itr = venues.begin(); itr != venues.end(); itr++ ) {
			if ( itr->first != "venue" ) {
//				WARN<<"reading venues: contains unrecongnised element .."<<itr->first;
				continue;
			}

			this->venues.push_back(itr->second.get_value<std::string>());
		}

	}

	friend std::ostream& operator<<(std::ostream &os, const TradeAccount &tad) {
//		os<<tad.username<<" "<<tad.dll_ip<<" "<<tad.dll_str5;
		return os;
	}

	void stop() {

		for (auto itr = trade_instances.begin(); itr != trade_instances.end(); itr++ ) {
			itr->second->stopAction();
		}

		short_account->CancelAllOrders();
		long_account->CancelAllOrders();
	}

	uint32_t getMaximumLiveLimit() const {
		return maximum_live;
	}

	void start() {
		for (auto itr = trade_instances.begin(); itr != trade_instances.end(); itr++ ) {
			itr->second->startAction();
		}
	}

	void stop(std::string &symbol) {
		if ( trade_instances.find(symbol) != trade_instances.end() ) {
			trade_instances[symbol]->stopAction();
		} else {
			WARN<<"no symbol under account found "<<symbol;
		}
	}

	void start(std::string &symbol) {
		if ( trade_instances.find(symbol) != trade_instances.end() ) {
			trade_instances[symbol]->startAction();
		} else {
			WARN<<"no symbol under account found "<<symbol;
		}
	}


	void enablePanicClose(const std::string &symbol = "" ) {
		for (auto itr = trade_instances.begin(); itr != trade_instances.end(); itr++ ) {
			if ( symbol == "" || symbol == itr->first )
				itr->second->enablePanicClose();
		}
	}

	void disablePanicClose(const std::string &symbol = "" ) {
		for (auto itr = trade_instances.begin(); itr != trade_instances.end(); itr++ ) {
			if ( symbol == "" || symbol == itr->first )
				itr->second->disablePanicClose();
		}
	}

	void enableSoftClose(const std::string &symbol = "" ) {
		for (auto itr = trade_instances.begin(); itr != trade_instances.end(); itr++ ) {
			if ( symbol == "" || symbol == itr->first )
				itr->second->enableSoftClose();
		}
	}

	void disableSoftClose(const std::string &symbol = "" ) {
		for (auto itr = trade_instances.begin(); itr != trade_instances.end(); itr++ ) {
			if ( symbol == "" || symbol == itr->first )
				itr->second->disableSoftClose();
		}
	}

	void avicennaToggle() {
		std::for_each(trade_instances.begin(), trade_instances.end(), [](TradeInstances::value_type &value){
			auto& symbol = value.first;
			auto& trade_instance = *value.second;
			trade_instance.avicennaToggle();
		});
	}

};


class TradeAccountMap: public unordered_map<string, unique_ptr<TradeAccount>> {
public:
	TradeAccountMap(ptree &pt) {
		for (auto itr = pt.begin(); itr != pt.end(); itr++ ) {
			if ( "account" == itr->first ) {
				std::unique_ptr<TradeAccount> trade_account(new TradeAccount(itr->second));
				string id = trade_account->id;
				emplace(id, std::move(trade_account));
			} else {
				WARN<<"trade configuration files expect <account> configurations";
			}
		}
	}
};

class TradingConfig {
public:

	static TradeAccountMap* trade_accounts_map;

	virtual ~TradingConfig(){
	}

	static void parse(string filename) {
		static std::once_flag only_once;
		std::call_once(only_once, [filename](){
			ptree pt;
			boost::property_tree::xml_parser::read_xml(filename, pt);
			int count = pt.count("trading");
			if ( 0 == count  ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("Missing <trading> tag"));
			else if ( 1 < count ) BOOST_THROW_EXCEPTION(ConfigException()<<config_info("More than one definition of <trading>"));

			ptree &trading = pt.get_child("trading");
			trade_accounts_map = new TradeAccountMap(trading);
			if ( nullptr == trade_accounts_map )  BOOST_THROW_EXCEPTION(ConfigException()<<config_info("failed to allocate memory for user trading table"));
		});

	}


private:
	TradingConfig() {
	}
};

#endif /* TRADINGCONFIG_H_ */
